import asyncio
import aiofiles
from argparse import ArgumentParser
import configparser
import glob
import pathlib
from loguru import logger
from typing import List, Dict, NewType, TypedDict
import os

class Pacenote(TypedDict):
    name: str
    id: int
    sounds_count: int
    sounds: List[str]
    column: int
    link: str
    standard: str

class Category(TypedDict):
    name: str
    descriptor_file: pathlib.Path
    pacenotes: List[Pacenote]

class Package(TypedDict):
    filename: str
    name: str
    category_files: pathlib.Path
    categories: List[Category]

OUTPUT_PATH = pathlib.Path('out')

async def get_pacenotes_from_category(category: Category):
    result = []
    async with aiofiles.open(category['descriptor_file'], mode='r') as f:
        content = await f.read()
        parser = configparser.ConfigParser(strict=False)
        parser.read_string(content)
        for section in parser.sections():
            if section.startswith('PACENOTE::'):
                # ok, this is a pacenote
                pacenote_name = section[10:]
                p = { 'name': pacenote_name, 'id': 0, 'sounds_count': 0, 'sounds': [], 'column': 0, 'link': '', 'standard': '' }
                for k in parser[section].keys():
                    if k.startswith('id'):
                        p['id'] = int(parser[section][k])
                    elif k.startswith('sounds'):
                        p['sounds_count'] = int(parser[section][k])
                    elif k.startswith('snd'):
                        p['sounds'].append(parser[section][k])
                    elif k.startswith('column'):
                        p['column'] = int(parser[section][k])
                    elif k.startswith('link'):
                        p['link'] = parser[section][k]
                    elif k.startswith('standard'):
                        p['standard'] = parser[section][k]
                result.append(p)

    logger.info(f'found {len(result)} pacenotes in {category["name"]}')
    return result

async def get_categories_from_package(pkg: Package):
    result = []
    for category in pkg['category_files']:
        async with aiofiles.open(category, mode='r') as f:
            content = await f.read()
            parser = configparser.ConfigParser()
            parser.read_string(content)
            for section in parser.sections():
                if section.startswith('CATEGORY::'):
                    # ok, this is a category
                    category_name = section[10:]
                    c = { 'name': category_name, 'descriptor_file': '', 'pacenotes': [] }
                    if parser[section].get('file'):
                        descriptor_file = parser[section]['file'].replace('\\', '/')
                        c['descriptor_file'] = pathlib.Path(category).parent / descriptor_file
                    
                    result.append(c)

    logger.info(f'found {len(result)} categories in {pkg["name"]}')
    return result

async def get_packages(args: dict, packages_folder: str) -> List[Package]:
    result = []
    rbr_folder = args.get('folder')
    packages_folder = rbr_folder + '/' + packages_folder
    for file in glob.glob(packages_folder + '/*.ini'):
        async with aiofiles.open(file, mode='r') as f:
            content = await f.read()
            parser = configparser.ConfigParser()
            parser.read_string(content)
            for section in parser.sections():
                if section.startswith('PACKAGE::'):
                    # ok, this is a package
                    package_name = section[9:]
                    pkg = { 'name': package_name, 'category_files': [], 'categories': [], 'filename': pathlib.Path(file).stem}
                    for k in parser[section].keys():
                        if k.startswith('file'):
                            # this is the category definition file
                            category_file = parser[section][k].replace('\\', '/')
                            pkg['category_files'].append(pathlib.Path(packages_folder) / category_file)
                    result.append(pkg)

    logger.info(f'found {len(result)} packages in {packages_folder}')
    return result

async def get_descriptions(args: dict):
    result = {}
    # descriptions are in /Plugins/Pacenote/language/english, all ini files
    rbr_folder = args.get('folder')
    descriptions_folder = rbr_folder + '/Plugins/Pacenote/language/english'
    for file in glob.glob(descriptions_folder + '/**/*.ini', recursive=True):
        async with aiofiles.open(file, mode='r') as f:
            content = await f.read()
            # remove empty lines which cannot be recognized by configparser
            content = os.linesep.join([s for s in content.splitlines() if s])
            # remove first line
            content = content[content.find('\n') + 1:]
            parser = configparser.ConfigParser(strict=False)
            parser.optionxform = str
            try:
                parser.read_string(content)
                for section in parser.sections():
                    for k in parser[section].keys():
                        result[k] = parser[section][k]
            except Exception as e:
                logger.error(f'Error parsing {file}: {e}')
                pass

    return result

async def output_to_csv(args: dict, pkgs: List[Package], descriptions: dict):
    # output to csv, one file per package
    OUTPUT_PATH.mkdir(parents=True, exist_ok=True)
    for pkg in pkgs:
        with open(OUTPUT_PATH / f'{pkg["filename"]}.csv', 'w') as f:
            f.write('Id,Pacenote,Sound count,Column,Link,Standard,Sounds,Package,Category,Description\n')
            for category in pkg['categories']:
                for pacenote in category['pacenotes']:
                    f.write(f'{pacenote["id"]},{pacenote["name"]},{pacenote["sounds_count"]},{pacenote["column"]},{pacenote["link"]},{pacenote["standard"]},{";".join(pacenote["sounds"])},{pkg["name"]},{category["name"]},{descriptions.get(pacenote["name"], "")}\n')
            

async def main():
    parser = ArgumentParser()
    parser.add_argument('-f', '--folder', help='RBR installation folder or other folder contains the Plugins/Pacenote folder')
    args = parser.parse_args()
    args = vars(args)

    descriptions = await get_descriptions(args)
    
    pkgs = await get_packages(args, 'Plugins/Pacenote/config/pacenotes')
    # pkgs = pkgs + await get_packages(args, 'Plugins/Pacenote/config/ranges')
    for pkg in pkgs:
        pkg['categories'] = await get_categories_from_package(pkg)
        for category in pkg['categories']:
            category['pacenotes'] = await get_pacenotes_from_category(category)
    
    await output_to_csv(args, pkgs, descriptions)

    pass

if __name__ == '__main__':
    asyncio.run(main())
